//////////////////////////////////////////////////////////////////////////////
//
//  Created by Philip Mulcahy on 08/01/2012.
//  Copyright (c) 2012 Philip Mulcahy. All rights reserved.
//
//  This file is part of the note-recog library.
//
//  note-recog is free software: you can redistribute it and/or modify
//  it under the terms of version 3 of the GNU Lesser General Public License 
//  as published by the Free Software Foundation.
//
//  note-recog is distributed is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with note-recog.  If not, see <http://www.gnu.org/licenses/>.
//
//////////////////////////////////////////////////////////////////////////////

#import "FftTest.h"
#import <vector>
#import "Slice.h"
#import "Harmonic.h"
#import <cassert>
#import "FastFourierTransform.h"
#import "Spectrum.h"

using namespace std;

@implementation FftTest

- (void)testFFT1
{
    vector<short> timeDomain;
    for( size_t i=0; i!=32; ++i )
    {
        timeDomain.push_back(i%2);
    }
    
    FastFourierTransform fft(timeDomain,22050.f);
    
    cout << "testFFT1" << endl;
    const Spectrum spec = fft.calculate();
    size_t i = 0;
    for( vector<float>::const_iterator it = spec.abs().begin();
        it != spec.abs().end();
        ++it
        )
    {
        cout << i++ << " " << *it << " " << *(++it) << endl;
    }
    
    STAssertEqualsWithAccuracy(*(spec.abs().begin()), 45.255f, 0.001f, @"DC hypot with Nyquist");
    STAssertEqualsWithAccuracy(*(spec.abs().begin()+1), -0.f, 0.001f, @"lowest non-DC component");
    
    for( size_t i1 = 2; i!=timeDomain.size(); ++i ) {
        STAssertEqualsWithAccuracy(*(spec.abs().begin()+i1), 0.f, 0.001f, @"DC value");
    }
}


- (void)testFFT2
{
    vector<short> timeDomain;
    for( size_t i=0; i!=8; ++i )
    {
        timeDomain.push_back(1-i%2);
    }
    
    FastFourierTransform fft(timeDomain,22050.f);
    
    const Spectrum spec = fft.calculate();
    
    cout << "testFFT2" << endl;
    size_t i = 0;
    for( vector<float>::const_iterator it = spec.abs().begin();
        it != spec.abs().end();
        ++it
        )
    {
        cout << i++ << " " << *it << " " << *(++it) << endl;
    }
    
    STAssertEqualsWithAccuracy(*(spec.abs().begin()), 11.314f, 0.001f, @"DC value hypot with Nyquist");
    STAssertEqualsWithAccuracy(*(spec.abs().begin()+1), 0.f, 0.001f, @"lowest non DC");
    
    for( size_t i1 = 2; i!=timeDomain.size(); ++i ) {
        STAssertEqualsWithAccuracy(*(spec.abs().begin()+i1), 0.f, 0.001f, @"DC value");
    }
}


- (void) testHighFreq
{
    vector<short> time_domain;
    for( int i = 0; i != 16; ++i ) {
        time_domain.push_back(1); 
        time_domain.push_back(-1);
    }

    FastFourierTransform fft( time_domain, 22050.f );
    const Spectrum spec = fft.calculate();
    
    for( vector<float>::const_iterator it = spec.abs().begin();
         it != spec.abs().end();
         ++it ) {
        
        if( it == spec.abs().begin() )
        {
            STAssertEqualsWithAccuracy( 64.f, *it, 0.001, @"first" );
        }
        else
        {
            STAssertEqualsWithAccuracy( 0.f, *it, 0.001, @"subsequent" );
        }
    }
}

- (void) testMedFreq
{
    vector<short> time_domain;
    for( int i = 0; i != 8; ++i ) {
        time_domain.push_back(1);
        time_domain.push_back(1);
        time_domain.push_back(-1);
        time_domain.push_back(-1);
    }
    
    FastFourierTransform fft( time_domain, 22050.f );
    const Spectrum spec = fft.calculate();
    
    size_t i = 0;
    for( vector<float>::const_iterator it = spec.abs().begin();
        it != spec.abs().end();
        ++it ) {
        
        if( it == spec.abs().begin() + 8 )
        {
           STAssertEqualsWithAccuracy( 45.2548f, *it, 0.001, @"ninth" );
        }
        else
        {
           STAssertEqualsWithAccuracy( 0.f, *it, 0.001, @"others" );
        }
        
        ++i;
    }
    
    STAssertEqualsWithAccuracy( 1.875f, spec.quality(), 0.01, @"quality" );
}

- (void) testQualityAndRms
{
    vector<short> freq_domain;
    for( int i = 0; i != 8; ++i ) {
        freq_domain.push_back(1);
        freq_domain.push_back(1);
        freq_domain.push_back(-1);
        freq_domain.push_back(-1);
    }
    
    const Spectrum spec( freq_domain.begin(), freq_domain.end(), 22050.f );
    
    const float rms = spec.rms();
    
    STAssertEqualsWithAccuracy( 1.f, rms, 0.01, @"quality" );
    
    const float countdiff = freq_domain.size() - 1;
    const float meanabsdiff = ( 15.f * 2.0f ) / countdiff;
    const float expectedquality = rms / meanabsdiff;
    
    STAssertEqualsWithAccuracy( expectedquality, spec.quality(), 0.01, @"quality" );
}

@end
